﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using WMM.Tapioca.Linkbase;
using System.Xml.Schema;
using System.Xml;
using WMM.Tapioca.Instance;
using WMM.Tapioca.Taxonomy;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using WMM.Tapioca.XbrlDimension;
using WMM.Tapioca.Utilities;
using System.Xml.Serialization;
using System.IO;

namespace WMM.Tapioca.Taxonomy
{
    /// <summary>
    /// XBRL 2.1 Specification 3.2
    /// 
    /// A DTS is a collection of taxonomy schemas and linkbases. The bounds of a DTS are such that 
    /// the DTS includes all taxonomy schemas and linkbases that can be discovered by following 
    /// links or references in the taxonomy schemas and linkbases included in the DTS. 
    /// At least one taxonomy schema in a DTS must import the xbrl-instance-2003-12-31.xsd schema.
    /// 
    /// Although an XBRL instance can be the starting point for DTS discovery, the XBRL instance itself 
    /// is not part of the DTS. Taxonomy schemas and linkbases that are used as starting points for DTS 
    /// discovery are part of the DTS that they discover.
    /// 
    /// DTS rules of discovery:
    /// Taxonomy schemas in the DTS are those:
    ///    1.    referenced directly from an XBRL instance using the schemaRef, roleRef, arcroleRef or linkbaseRef element. 
    ///          The xlink:href attribute on the schemaRef, roleRef, arcroleRef or linkbaseRef element contains the URL of 
    ///          the taxonomy schema that is discovered. Every taxonomy schema that is referenced by the schemaRef, roleRef, 
    ///          arcroleRef or linkbaseRef element MUST be discovered.
    ///    2.    referenced from a discovered taxonomy schema via an XML Schema import or include element. Every taxonomy 
    ///          schema that is referenced by an import or include element in a discovered taxonomy schema MUST be discovered.
    ///          NOTE: since <redefine> is prohibited in taxonomy schemas it cannot play a role in DTS discovery.
    ///    3.    referenced from a discovered linkbase document via a loc element. Every taxonomy schema that is referenced by
    ///          an xlink:href attribute on a loc element in a discovered linkbase MUST be discovered.
    ///    4.    referenced from a discovered linkbase document via a roleRef element. Every taxonomy schema that is referenced
    ///          by an xlink:href attribute on a roleRef element in a discovered linkbase MUST be discovered.
    ///    5.    referenced from a discovered linkbase document via an arcroleRef element. Every taxonomy schema that is referenced
    ///          by an xlink:href attribute on an arcroleRef element in a discovered linkbase MUST be discovered.
    ///    6.    referenced from a discovered taxonomy schema via a linkbaseRef element. Every taxonomy schema that is referenced by
    ///          an xlink:href attribute on a linkbaseRef element in a discovered taxonomy schema MUST be discovered. 
    ///
    ///    Linkbase documents in the DTS are those:
    ///    1.    referenced directly from an XBRL instance via the linkbaseRef element. The xlink:href attribute contains the URL 
    ///          of the linkbase document being discovered. Every linkbase that is referenced by the linkbaseRef element MUST be discovered.
    ///    2.    referenced from a discovered taxonomy schema via the linkbaseRef element. The xlink:href attribute contains the 
    ///          URL of the linkbase being discovered. Every linkbase that is referenced by the linkbaseRef element MUST be discovered.
    ///    3.    that are among the set of nodes identified by the XPath [XPATH] path "//xsd:schema/xsd:annotation/xsd:appinfo/*" 
    ///          in a discovered taxonomy schema (Throughout this specification, schema, annotation and appinfo are all elements 
    ///          defined in the XML Schema namespace).
    ///    4.    referenced from a discovered linkbase document via a loc element. Every linkbase that contains a resource that
    ///          is referenced by an xlink:href attribute on a loc element in a discovered linkbase MUST be discovered.
    ///
    /// </summary>
    public class DTS
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private bool validatedSchema = true;

        private DTSDimension dtsDimension;

        public DTSDimension DtsDimension
        {
            get { return dtsDimension; }
            set { dtsDimension = value; }
        }

        private DTSFormula dtsFormula;

        public DTSFormula DtsFormula
        {
            get { return dtsFormula; }
            set { dtsFormula = value; }
        }
        /// <summary>
        /// Flag indicating that the DTS was validated.
        /// </summary>
        public bool ValidatedSchema
        {
            get { return validatedSchema; }
            set { validatedSchema = value; }
        }

        private bool validatedInstance = true;

        /// <summary>
        /// Flag indicating that the instance was validated.
        /// </summary>
        public bool ValidatedInstance
        {
            get { return validatedInstance; }
            set { validatedInstance = value; }
        }

        private bool validatedLinkbases = true;

        /// <summary>
        /// Flag indicating that the linkbases were validated.
        /// </summary>
        public bool ValidatedLinkbases
        {
            get { return validatedLinkbases; }
            set { validatedLinkbases = value; }
        }

        private bool validatedCalculation = true;

        /// <summary>
        /// Flag indicating that the linkbase calculation was validated.
        /// </summary>
        public bool ValidatedCalculation
        {
            get { return validatedCalculation; }
            set { validatedCalculation = value; }
        }

        private bool validatedDimension = true;

        /// <summary>
        /// Flag indicating that the taxonomy dimensional was validated.
        /// </summary>
        public bool ValidatedDimension
        {
            get { return validatedDimension; }
            set { validatedDimension = value; }
        }

        private bool validatedFormula = true;

        /// <summary>
        /// Flag indicating that the taxonomy dimensional was validated.
        /// </summary>
        public bool ValidatedFormula
        {
            get { return validatedFormula; }
            set { validatedFormula = value; }
        }

        private InstanceDocument instance;

        /// <summary>
        /// Represents a Instance Fragment.
        /// </summary>
        public InstanceDocument Instance
        {
            get { return instance; }
            set { instance = value; }
        }
        
        private MultiMap<Arc> baseSetsArcs;

        /// <summary>
        /// XBRL 2.1 Specification 3.5.3.9.7.3
        /// Represents a base sets of arcs.
        /// Each base set of arcs in a DTS represents the set of candidates for inclusion in a network of relationships.
        /// </summary>
        public MultiMap<Arc> BaseSetsArcs
        {
            get { return baseSetsArcs; }
            set { baseSetsArcs = value; }
        }

        private MultiMap<Arc> networkRelationships;

        /// <summary>
        /// For each base set of arcs in a DTS, the rules of relationship prohibition and overriding determine 
        /// the subset of relationships in that base set that participate in the corresponding network of 
        /// relationships represented by arcs in the DTS.
        /// </summary>
        public MultiMap<Arc> NetworkRelationships
        {
            get { return networkRelationships; }
            set { networkRelationships = value; }
        }

        private List<NetworkGraph> networkGraphs;

        /// <summary>
        /// Represents a Network of Graphs.
        /// </summary>
        public List<NetworkGraph> NetworkGraphs
        {
            get { return networkGraphs; }
            set { networkGraphs = value; }
        }
        
        private List<LinkbaseDocument> linkbaseDocuments;

        /// <summary>
        /// A collection of <see cref="LinkbaseDocument"/> objects representing all linkbase documents defined in the schema.
        /// </summary>
        public List<LinkbaseDocument> LinkbaseDocuments
        {
            get
            {
                return linkbaseDocuments;
            }
            set
            {
                linkbaseDocuments = value;
            }
        }
         

        private List<TaxonomySchema> taxonomySchemas;

        /// <summary>
        /// A collection of <see cref="TaxonomySchema"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<TaxonomySchema> TaxonomySchemas
        {
            get
            {
                return taxonomySchemas;
            }
            set
            {
                taxonomySchemas = value;
            }
        }


        private List<XmlSchemaSet> taxonomySchemasSet;

        /// <summary>
        /// A collection of <see cref="TaxonomySchema"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<XmlSchemaSet> TaxonomySchemasSet
        {
            get
            {
                return taxonomySchemasSet;
            }
            set
            {
                taxonomySchemasSet = value;
            }
        }


        private List<RoleRef> roleRefs;

        /// <summary>
        /// A collection of <see cref="RoleRef"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<RoleRef> RoleRefs
        {
            get
            {
                return roleRefs;
            }
            set
            {
                roleRefs = value;
            }
        }

        
        private List<ArcRoleRef> arcRoleRefs;

        /// <summary>
        /// A collection of <see cref="ArcRoleRef"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<ArcRoleRef> ArcRoleRefs
        {
            get
            {
                return arcRoleRefs;
            }
            set
            {
                arcRoleRefs = value;
            }
        }

        
        private List<RoleType> roleTypes;

        /// <summary>
        /// A collection of <see cref="RoleType"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<RoleType> RoleTypes
        {
            get
            {
                return roleTypes;
            }
            set
            {
                roleTypes = value;
            }
        }

        
        private List<ArcRoleType> arcRoleTypes; // ENCAPSULATE FIELD BY CODEIT.RIGHT

        /// <summary>
        /// A collection of <see cref="ArcRoleType"/> objects representing all chema documents defined in the taxonomy.
        /// </summary>
        public List<ArcRoleType> ArcRoleTypes
        {
            get
            {
                return arcRoleTypes;
            }
            set
            {
                arcRoleTypes = value;
            }
        }

        private XElement xbrlRootNode;

        /// <summary>
        /// The xbrl root node.
        /// </summary>
        public XElement XbrlRootNode
        {
            get { return xbrlRootNode; }
            set { xbrlRootNode = value; }
        }

        private XmlUrlResolver resolver;

        /// <summary>
        /// A class XmlUrlResolver resolver.
        /// </summary>
        public XmlUrlResolver Resolver
        {
            get { return resolver; }
            set { resolver = value; }
        }

        /// <summary>
        /// The XBRL instance as the starting point for DTS discovery.
        /// </summary>
        public DTS(InstanceDocument instanceDocument, XmlUrlResolver resolverDts) 
        {
            DateTime startTime = DateTime.Now;

            resolver = resolverDts;
            instance = instanceDocument;
            xbrlRootNode = instanceDocument.ThisXDocument.Root;

            if (!xbrlRootNode.Name.LocalName.Equals("xbrl"))
            {
                log.Error("Validation error on instance", new DTSDiscoveryException(instanceDocument.ThisXDocument.BaseUri + " is not a Taxonomy, Linkbase or XBRL Instance."));
                ValidatedInstance = false;
                return;
            }

            InicializeDTS();

            DiscoverySchemaInInstance();
            DiscoveryLinkbasesInInstance();
            DiscoverySchemaInXsiLocation(instance);  // UNDO: The XML Schema Instance schemaLocation attribute is no longer required in the DTS discovery process.
            DiscoverySchemaInRoleRefInstance(instanceDocument.ThisXDocument.Root);
            DiscoverySchemaInArcRoleRefInstance(instanceDocument.ThisXDocument.Root);

            DiscoveryDts();

            DateTime endTime = DateTime.Now;
            TimeSpan ts = endTime - startTime;
            //log.Info("DTS loaded in:" + ts.ToString(@"mm\:ss\.fff"));
            Console.WriteLine("[info] DTS loaded in:" + ts.ToString(@"mm\:ss\.fff"));
	    }


        /// <summary>
        /// The schema as the starting point for DTS discovery.
        /// </summary>
        public DTS(TaxonomySchema schema, XmlUrlResolver resolverDts)
        {
            resolver = resolverDts;

            InicializeDTS();

            DiscoveryDts(schema);
        }


        /// <summary>
        /// The linkbase as the starting point for DTS discovery.
        /// </summary>
        public DTS(LinkbaseDocument linkbase, XmlUrlResolver resolverDts)
        {
            if (!linkbase.ThisXDocument.Root.Name.LocalName.Equals("linkbase"))
            {
                log.Error("Validation error on instance", new DTSDiscoveryException(linkbase.ThisXDocument.BaseUri + " is not a Taxonomy, Linkbase or XBRL Instance."));
                ValidatedLinkbases = false;
                return;
            }

            resolver = resolverDts;

            InicializeDTS();

            DiscoveryDts(linkbase);
        }


        /// <summary>
        /// The xml instance as the starting point for DTS discovery.
        /// </summary>
        public DTS(XbrlDocument xbrlDocument, XmlUrlResolver resolverDts)
        {
            resolver = resolverDts;

            InicializeDTS();

            DiscoverySchemaInXsiLocation(xbrlDocument);
        }


        private void InicializeDTS()
        {
            LinkbaseDocuments = new List<LinkbaseDocument>();
            TaxonomySchemas = new List<TaxonomySchema>();
            TaxonomySchemasSet = new List<XmlSchemaSet>();
            RoleTypes = new List<RoleType>();
            ArcRoleTypes = new List<ArcRoleType>();
            RoleRefs = new List<RoleRef>();
            ArcRoleRefs = new List<ArcRoleRef>();
            baseSetsArcs = new MultiMap<Arc>();
            networkRelationships = new MultiMap<Arc>();
            networkGraphs = new List<NetworkGraph>();            
        }


        /// <summary>
        /// Discovery of DTS.
        /// </summary>
        public void DiscoveryDts()
        {
            // The linkbase as the starting point for DTS discovery.
            DiscoverySchemaInXsiLocationLinkbase();  // UNDO: The XML Schema Instance schemaLocation attribute is no longer required in the DTS discovery process.
            DiscoverySchemaInRoleRefLinkbase();
            DiscoverySchemaInArcRoleRefLinkbase();

            BuildRoleTypeAndArcRoleTypeInSchema();

            // Build Network Relationships
            BuildBaseSetsofArcs();
            BuildNetworkRelationships();
            BuildSummation();

            // Build Dimensional Taxonomy
            dtsDimension = new DTSDimension(this);
            
            //Build Formula Linkbase();
            dtsFormula = new DTSFormula(this);
        }


        /// <summary>
        /// Discovery of DTS.
        /// </summary>
        public void DiscoveryDts(TaxonomySchema schema)
        {
            TaxonomySchemas.Add(schema);
            TaxonomySchemasSet.Add(schema.XmlSchemaSet);

            DiscoverySchemaInXsiLocation(schema);

            DiscoveryLinkbaseInSchema(schema.XmlSchema);
            DiscoverySchemaInLinkbaseRef(schema);

            // The linkbase as the starting point for DTS discovery.
            DiscoverySchemaInXsiLocationLinkbase();  // UNDO: The XML Schema Instance schemaLocation attribute is no longer required in the DTS discovery process.
            DiscoverySchemaInRoleRefLinkbase();
            DiscoverySchemaInArcRoleRefLinkbase();

            BuildRoleTypeAndArcRoleTypeInSchema();

            // Build Network Relationships
            BuildBaseSetsofArcs();
            BuildNetworkRelationships();
            BuildSummation();

            // Build Dimensional Taxonomy
            dtsDimension = new DTSDimension(this);

            //Build Formula Linkbase();
            dtsFormula = new DTSFormula(this);
        }      


        /// <summary>
        /// Discovery of DTS.
        /// </summary>
        public void DiscoveryDts(LinkbaseDocument linkbase)
        {
            this.LinkbaseDocuments.Add(linkbase);

            DiscoverySchemaInLocator(linkbase);
            DiscoverySchemaInXsiLocation(linkbase);

            // The linkbase as the starting point for DTS discovery.
            DiscoverySchemaInXsiLocationLinkbase();  // UNDO: The XML Schema Instance schemaLocation attribute is no longer required in the DTS discovery process.
            DiscoverySchemaInRoleRefLinkbase();
            DiscoverySchemaInArcRoleRefLinkbase();

            BuildRoleTypeAndArcRoleTypeInSchema();

            // Build Network Relationships
            BuildBaseSetsofArcs();
            BuildNetworkRelationships();
            BuildSummation();

            // Build Dimensional Taxonomy
            dtsDimension = new DTSDimension(this);

            //Build Formula Linkbase();
            dtsFormula = new DTSFormula(this);
        }      
        

        private void DiscoverySchemaInInstance()
        {
            IEnumerable<XElement> schemaRefNodes = from c in xbrlRootNode.Elements()
                                                     where c.Name.LocalName.Equals("schemaRef")
                                                     select c;

            foreach (XElement schemaRefNode in schemaRefNodes)
            {
                ReadTaxonomySchemaRef(schemaRefNode);
            }
        }


        private void ReadTaxonomySchemaRef(XElement schemaRefElement)
        {
            XNamespace nsXLink = schemaRefElement.GetNamespaceOfPrefix("xlink");
            string hrefAttributeValue = (string)schemaRefElement.Attribute(nsXLink + "href") ?? string.Empty;

            XNamespace nsXml = (string)instance.NamespaceManager.LookupNamespace("xml") ?? string.Empty;
            string Base = (string)schemaRefElement.Attribute(nsXml + "base") ?? string.Empty;
            string baseUri = instance.ThisXDocument.Root.BaseUri;

            log.Info("Building the Discoverable Taxonomy Set (DTS).");
            log.Info("Creating Schema in instance: " + hrefAttributeValue);

            try
            {
                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + hrefAttributeValue));
                TaxonomySchema ts = new TaxonomySchema(this, hrefAttributeValue, instance, Base, this.resolver);            
                TaxonomySchemas.Add(ts);
                TaxonomySchemasSet.Add(ts.XmlSchemaSet);

                DiscoveryLinkbaseInSchema(ts.XmlSchema);
                DiscoverySchemaInLinkbaseRef(ts);
            }
            catch (Exception)
            {
                log.Error("Error in create Schema in instance: " + hrefAttributeValue);
                return;
            }
        }


        private void DiscoverySchemaInXsiLocation(XbrlDocument document)
        {
            // Discovery in instance document.
            XNamespace xsi = Constants.XSI;
            string schemaLocation = (string)document.ThisXDocument.Root.Attribute(xsi + "schemaLocation") ?? string.Empty; 

            if (schemaLocation != null)
            {
                schemaLocation = schemaLocation.TrimEnd().TrimStart();
                schemaLocation = Regex.Replace(schemaLocation, @"\s+", " ");
                string[] schemaLocationSplit = schemaLocation.ToString().Split(' ');

                if (schemaLocationSplit.Length > 1)
                {
                    string Base = (string)document.ThisXDocument.Root.Attribute("base") ?? string.Empty;
                    string baseUri = document.ThisXDocument.Root.BaseUri;

                    for (int i = 0; i < schemaLocationSplit.Length; i = i + 2)
                    {
                        string nameFile = schemaLocationSplit[i + 1];

                        if (!ContainSchema(nameFile))
                        {
                            log.Info("Creating Schema in document(" + document.FileName + ") attribute xsi:schemaLocation: " + nameFile);

                            try
                            {
                                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + nameFile));
                                TaxonomySchema ts = new TaxonomySchema(this, nameFile, document, Base, this.resolver);
                                TaxonomySchemas.Add(ts);
                                TaxonomySchemasSet.Add(ts.XmlSchemaSet);
                                DiscoveryLinkbaseInSchema(ts.XmlSchema);
                                DiscoverySchemaInLinkbaseRef(ts);
                            }
                            catch (Exception)
                            {
                                log.Error("Error in create Schema in document(" + document.FileName + ") attribute xsi:schemaLocation: " + nameFile);
                                continue;
                            }
                        }
                        else
                        {
                            // Load linkbase referended in element linkbaseRef.
                            //TaxonomySchema ts = new TaxonomySchema(this, nameFile, instance, Base, this.resolver);
                            //DiscoveryLinkbaseInSchema(ts);
                        }
                    }
                }
            }
        }


        private void DiscoverySchemaInXsiLocationLinkbase()
        {
            // Discovery in linkbase document.
            foreach (LinkbaseDocument linkbase in LinkbaseDocuments)
            {
                XNamespace xsi = Constants.XSI;
                string schemaLocation = (string)linkbase.ThisXDocument.Root.Attribute(xsi + "schemaLocation") ?? string.Empty;

                if (schemaLocation != null)
                {
                    schemaLocation = schemaLocation.TrimEnd().TrimStart().Replace("  ", " ");
                    schemaLocation = Regex.Replace(schemaLocation, @"\s+", " ");
                    string[] schemaLocationSplit = schemaLocation.ToString().Split(' ');

                    if (schemaLocationSplit.Length > 1)
                    {
                        string Base = (string)linkbase.ThisXDocument.Root.Attribute("base") ?? string.Empty;
                        string baseUri = linkbase.ThisXDocument.Root.BaseUri;

                        for (int i = 0; i < schemaLocationSplit.Length; i = i + 2)
                        {
                            string nameFile = schemaLocationSplit[i + 1];

                            if (!ContainSchema(nameFile))
                            {
                                log.Info("Creating Schema in linkbase attribute xsi:schemaLocation: " + nameFile);

                                try
                                {
                                    Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + nameFile));
                                    TaxonomySchema ts = new TaxonomySchema(this, nameFile, linkbase, Base, this.resolver);
                                    TaxonomySchemas.Add(ts);
                                    TaxonomySchemasSet.Add(ts.XmlSchemaSet);
                                    DiscoveryLinkbaseInSchema(ts.XmlSchema);
                                    DiscoverySchemaInLinkbaseRef(ts);
                                }
                                catch (Exception)
                                {
                                    log.Error("Error in create Schema in linkbase(" + linkbase.FileName + ") attribute xsi:schemaLocation: " + nameFile);
                                    throw;
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Discovery of linkbase in schema file.
        /// </summary>
        public void DiscoveryLinkbaseInSchema(XmlSchema xmlSchema)
        {
            //string linkbaseNamespacePrefix = xmlSchema.NamespaceManager.LookupPrefix(Constants.LINK)            

            string linkbaseNamespacePrefix = LookupPrefix(xmlSchema, Constants.LINK);

            if (linkbaseNamespacePrefix != null && linkbaseNamespacePrefix.Length == 0)
            {
                xmlSchema.Namespaces.Add("link", "http://www.xbrl.org/2003/linkbase");
                linkbaseNamespacePrefix = "link";
            }

            foreach (XmlSchemaObject item in xmlSchema.Items)
            {
                if (item is XmlSchemaAnnotation)
                {
                    string baseAnnotation = "";

                    if ((item as XmlSchemaAnnotation).UnhandledAttributes != null)
                    {
                        foreach (XmlAttribute attr in (item as XmlSchemaAnnotation).UnhandledAttributes)
                        {
                            if (attr.LocalName.Equals("base"))
                            {
                                baseAnnotation = attr.Value;
                            }
                        }
                    }

                    foreach (var annotation in ((XmlSchemaAnnotation)item).Items)
                    {
                        if (annotation is XmlSchemaAppInfo)
                        {
                            XmlSchemaAppInfo appinfo = annotation as XmlSchemaAppInfo;
                            
                            
                            string baseAppinfo = "";
                            string baseAttr = "";

                            string schemaName = Path.GetFileName(xmlSchema.SourceUri);

                            TaxonomySchema taxonomySchema = GetSchemaFromTaxonomy(schemaName);

                            if (taxonomySchema != null)
                            {
                                baseAttr = taxonomySchema.BaseAttr;

                                XNamespace ns = "http://www.w3.org/2001/XMLSchema";

                                XElement appinfoElement = taxonomySchema.ThisXDocument.Descendants(ns + "appinfo").First();

                                if (appinfoElement != null)
                                {
                                    XNamespace nsXml = "http://www.w3.org/XML/1998/namespace";
                                    baseAppinfo = (string)appinfoElement.Attribute(nsXml + "base") ?? string.Empty;
                                }
                            }

                            foreach (XmlNode element in appinfo.Markup)
                            {
                                if (element.LocalName.Equals("linkbaseRef"))
                                {
                                    string role = XmlUtilities.GetAttributeValue(element, "xlink:role");
                                    XmlNode attrHref = element.Attributes.GetNamedItem("xlink:href");
                                    string baseLinkbaseRef = XmlUtilities.GetAttributeValue(element, "xml:base");

                                    if (attrHref != null)
                                    {
                                        if (!ContainsLinkbase(attrHref))
                                        {
                                            log.Info("Creating LinkbaseDocument(" + attrHref.Value + ") in schema.");
                                            LinkbaseDocument linkbase = null;

                                            string basePath = ResolveRelativeBaseUri(baseAttr, baseAnnotation, baseAppinfo, baseLinkbaseRef);

                                            try
                                            {
                                                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + attrHref.Value));
                                                linkbase = new LinkbaseDocument(this, xmlSchema, basePath, attrHref.Value, role, this.resolver);
                                            }
                                            catch (XmlException ex)
                                            {
                                                log.Error("Validation error on create linkbase", ex);
                                                ValidatedLinkbases = false;
                                                return;
                                            }

                                            if (!linkbase.ThisXDocument.Root.Name.LocalName.Equals("linkbase"))
                                            {
                                                log.Error("Validation error on DTS", new DTSDiscoveryException(linkbase.ThisXDocument.BaseUri + " is not a Taxonomy, Linkbase or XBRL Instance."));
                                                ValidatedInstance = false;
                                            }

                                            this.LinkbaseDocuments.Add(linkbase);

                                            DiscoverySchemaInLocator(linkbase);
                                        }
                                    }
                                }


                                // Discovery linkbase in schema file

                                if (element.LocalName.Equals("linkbase"))
                                {
                                    log.Info("Creating LinkbaseDocument in schema file:" + xmlSchema.SourceUri + " - tag linkbase.");

                                    NameTable nameTable = new NameTable();
                                    XmlNamespaceManager nsmanager = new XmlNamespaceManager(nameTable);

                                    foreach (XmlQualifiedName xname in xmlSchema.Namespaces.ToArray())
                                    {
                                        nsmanager.AddNamespace(xname.Name, xname.Namespace);
                                    }

                                    XmlNode roleRef = element.SelectSingleNode("//link:roleRef", nsmanager);
                                    string role = "";

                                    if (roleRef != null)
                                    {
                                        role = roleRef.Attributes.GetNamedItem("roleURI").Value;
                                    }

                                    LinkbaseDocument lb = new LinkbaseDocument(this, xmlSchema, element, role, this.resolver);
                                    this.LinkbaseDocuments.Add(lb);

                                    DiscoverySchemaInLocator(lb);

                                    //DiscoverySchemaInXsiLocation(xmlSchema);
                                }
                            }
                        }
                    }
                }
            }            

            //verify in import and include elements.
            foreach (XmlSchemaObject schema in xmlSchema.Includes)
            {
                XmlSchema externalSchema = null;

                if (schema is XmlSchemaImport)
                {
                    externalSchema = ((XmlSchemaImport)schema).Schema;

                    //if (!externalSchema.TargetNamespace.StartsWith("http://xbrl.org") && externalSchema.SourceUri.StartsWith("file:///"))
                    if (!externalSchema.SourceUri.StartsWith("http://www.xbrl.org/"))
                    {
                        DiscoveryLinkbaseInSchema(externalSchema);
                    }
                }
            }
        }


        private TaxonomySchema GetSchemaFromTaxonomy(string schemaName)
        {
            foreach (TaxonomySchema taxonomyschema in TaxonomySchemas)
            {
                if (taxonomyschema.FileName.Equals(schemaName))
                {
                    return taxonomyschema;
                }
            }

            return null;
        }


        private string LookupPrefix(XmlSchema xmlSchema, string ns)
        {
            foreach (XmlQualifiedName xname in xmlSchema.Namespaces.ToArray())
            {
                if (xname.Namespace.Equals(ns))
                {
                    return xname.Name;
                }
            }

            return null;
        }


        // Resolving Relative URIs
        private string ResolveRelativeBaseUri(string baseAttr, string baseAnnotation, string baseAppinfo, string baseLinkbaseRef)
        {
            //if (baseLinkbaseRef.Length > 0)
            //{
            //    if (baseLinkbaseRef.StartsWith("/") || baseLinkbaseRef.StartsWith("./"))
            //    {
            //        return baseAnnotation + baseLinkbaseRef;
            //    }
            //    else
            //    {
            //        return baseAnnotation + baseAppinfo + baseLinkbaseRef;
            //    }
            //}
            //else
            //{
            //    return baseAnnotation + baseAppinfo;
            //}

            return baseAttr + baseAnnotation + baseAppinfo + baseLinkbaseRef;
        }


        private bool ContainsLinkbase(XmlNode attrHref)
        {
            string nameLinkbase = GetFileName(attrHref.Value);

            foreach (LinkbaseDocument linkbaseDocument in LinkbaseDocuments)
            {
                if (linkbaseDocument.FileName.Equals(nameLinkbase))
                {
                    return true;
                }
            }

            return false;
        }
 

        /// <summary>
        /// Section 3.2 of the XBRL 2.1 Specification
        /// Referenced from a discovered linkbase document via a loc element. Every taxonomy schema that is referenced
        /// by an xlink:href attribute on a loc element in a discovered linkbase MUST be discovered.
        /// </summary>
        private void DiscoverySchemaInLocator(LinkbaseDocument linkbase)
        {
            foreach (ExtendedLink extendedLink in linkbase.ExtendedLinks)
            {
                foreach (Locator loc in extendedLink.Locators)
                {
                    HyperlinkReference xp = new HyperlinkReference(loc.Href);

                    if (!(xp.Url.Length == 0))
                    {
                        if (!ContainSchema(xp.Url))
                        {
                            log.Info("Creating Schema referenced from a locator element: " + xp.Url);

                            try
                            {
                                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + linkbase.BaseAttr + xp.Url));
                                TaxonomySchemas.Add(new TaxonomySchema(this, linkbase.BaseAttr + xp.Url, linkbase, "", this.resolver));
                            }
                            catch (Exception)
                            {
                                log.Error("Error in create Schema referenced from a locator element(" + loc.Label + "): " + xp.Url);
                                continue;
                            }
                        }
                    }
                }
            }
        }


        private bool ContainSchema(string pathFile)
        {
            string name = GetFileName(pathFile);

            foreach (TaxonomySchema taxonomyschema in TaxonomySchemas)
            {
                if (!pathFile.StartsWith("http://"))
                {
                    if (taxonomyschema.FileName.ToLower().Equals(name.ToLower()) || name.EndsWith(".xml"))
                    {
                        return true;
                    }
                }
                else
                {
                    if (taxonomyschema.PathFile.ToLower().Equals(pathFile.ToLower()) || name.EndsWith(".xml"))
                    {
                        return true;
                    }
                }

                // verify in import and include elements.

                foreach (XmlSchemaObject schema in taxonomyschema.XmlSchema.Includes)
                {
                    XmlSchema externalSchema = null;

                    if (schema is XmlSchemaImport)
                    {
                        externalSchema = ((XmlSchemaImport)schema).Schema;

                        string schemaImportName = GetFileName(externalSchema.SourceUri);

                        if (schemaImportName.Equals(name) || schemaImportName.EndsWith(".xml"))
                        {
                            return true;
                        }
                    }

                    if (schema is XmlSchemaInclude)
                    {
                        externalSchema = ((XmlSchemaInclude)schema).Schema;

                        string schemaIncludeName = GetFileName(externalSchema.SourceUri);

                        if (schemaIncludeName.Equals(name) || schemaIncludeName.EndsWith(".xml"))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }


        internal string GetFileName(string href)
        {
            string nameFile = href;
            int lastPathSeparator = href.LastIndexOf('/');

            if (lastPathSeparator != -1)
            {
                nameFile = href.Substring(lastPathSeparator + 1);
            }

            return nameFile;
        }


        private void BuildRoleTypeAndArcRoleTypeInSchema()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Building RoleType and ArcRoleType in Schema"));

            foreach (TaxonomySchema taxonomySchema in TaxonomySchemas)
            {
                CreateRoleTypeAndArcRoleTypeInSchema(taxonomySchema.XmlSchema);

                //verify in import and include elements.
                foreach (XmlSchemaObject schema in taxonomySchema.XmlSchema.Includes)
                {
                    XmlSchema externalSchema = null;

                    if (schema is XmlSchemaImport)
                    {
                        externalSchema = ((XmlSchemaImport)schema).Schema;
                    }

                    if (schema is XmlSchemaInclude)
                    {
                        externalSchema = ((XmlSchemaInclude)schema).Schema;
                    }

                    if (externalSchema != null)
                    {
                        CreateRoleTypeAndArcRoleTypeInSchema(externalSchema);
                    }
                }
            }
        }


        private void CreateRoleTypeAndArcRoleTypeInSchema(XmlSchema schema)
        {
            foreach (XmlSchemaObject item in schema.Items)
            {
                if (item is XmlSchemaAnnotation)
                {
                    foreach (var annotation in ((XmlSchemaAnnotation)item).Items)
                    {
                        if (annotation is XmlSchemaAppInfo)
                        {
                            XmlSchemaAppInfo appinfo = annotation as XmlSchemaAppInfo;
                            foreach (XmlNode element in appinfo.Markup)
                            {
                                //RoleType
                                if (element.LocalName.Equals("roleType"))
                                {
                                    XmlNode attrRoleURI = element.Attributes.GetNamedItem("roleURI");
                                    XmlNode id = element.Attributes.GetNamedItem("id");                                    
                                    this.RoleTypes.Add(new RoleType(schema, element));
                                }

                                //ArcRoleType
                                if (element.LocalName.Equals("arcroleType"))
                                {
                                    XmlNode attrArcRoleURI = element.Attributes.GetNamedItem("arcroleURI");                                    
                                    this.ArcRoleTypes.Add(new ArcRoleType(schema, element));
                                }
                            }
                        }
                    }
                }
            }
        }


        private bool ContainRoleType(string roleUri)
        {
            foreach (RoleType roleType in RoleTypes)
            {
                if (roleType.RoleUri.ToString().Equals(roleUri))
                {
                    return true;
                }
            }

            return false;
        }


        private bool ContainArcRoleType(string arcRoleUri)
        {
            foreach (ArcRoleType arcRoleType in ArcRoleTypes)
            {
                if (arcRoleType.ArcRoleUri.ToString().Equals(arcRoleUri))
                {
                    return true;
                }
            }

            return false;
        }


        private void DiscoveryLinkbasesInInstance()
        {
            IEnumerable<XElement> linkbasesRefNodes = from c in xbrlRootNode.Elements()
                                                      where c.Name.LocalName.Equals("linkbaseRef")
                                                      select c;
            
            foreach (XElement element in linkbasesRefNodes)
            {
                XNamespace ns = "http://www.w3.org/1999/xlink";
                XNamespace nsXml = "http://www.w3.org/XML/1998/namespace";

                string arcrole = (string)element.Attribute(ns + "arcrole") ?? string.Empty; 
                XAttribute attrHref = element.Attribute(ns + "href");
                string baseAttr = (string)element.Attribute(nsXml + "base") ?? string.Empty; 

                if (attrHref != null)
                {
                    log.Info("Creating LinkbaseDocument(" + attrHref.Value + ") in schema.");
                    LinkbaseDocument linkbase = null;

                    try
                    {
                        Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + attrHref.Value));
                        linkbase = new LinkbaseDocument(this, instance, baseAttr, attrHref.Value, arcrole, this.resolver);
                    }
                    catch (XmlException ex)
                    {
                        log.Error("Validation error on create linkbase", ex);
                        ValidatedLinkbases = false;
                        return;
                    }
                    

                    if (!linkbase.ThisXDocument.Root.Name.LocalName.Equals("linkbase"))
                    {
                        log.Error("Validation error on DTS", new DTSDiscoveryException(linkbase.ThisXDocument.BaseUri + " is not a Taxonomy, Linkbase or XBRL Instance."));
                        ValidatedInstance = false;
                    }

                    this.LinkbaseDocuments.Add(linkbase);
                }         
            }
        }


        private void DiscoverySchemaInRoleRefInstance(XElement doc)
        {
            //RoleRef
            IEnumerable<XElement> rolesRefNodes = from c in doc.Elements()
                                                  where c.Name.LocalName.Equals("roleRef")
                                                  select c;

            foreach (XElement element in rolesRefNodes)
            {
                XNamespace ns = "http://www.w3.org/1999/xlink";
                string roleURI = (string)element.Attribute("roleURI") ?? string.Empty;
                XAttribute attrHref = element.Attribute(ns + "href"); 

                if (attrHref != null)
                {
                    this.RoleRefs.Add(new RoleRef(instance, attrHref.Value, roleURI));

                    HyperlinkReference xp = new HyperlinkReference(attrHref.Value);

                    if (!(xp.Url.Length == 0))
                    {
                        if (!ContainSchema(xp.Url))
                        {
                            log.Info("Creating Schema in roleref: " + xp.Url);

                            try
                            {
                                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + xp.Url + xp.Url));
                                TaxonomySchemas.Add(new TaxonomySchema(this, xp.Url, instance, "", this.resolver));
                            }
                            catch (Exception)
                            {
                                log.Error("Error in create Schema in roleref(" + roleURI + "): " + xp.Url);
                                continue;
                            }
                        }
                    }
                }
            }
        }


        private void DiscoverySchemaInArcRoleRefInstance(XElement doc)
        {
            //ArcRoleRef
            IEnumerable<XElement> arcRolesRefNodes = from c in doc.Elements()
                                                  where c.Name.LocalName.Equals("arcroleRef")
                                                  select c;
            
            foreach (XElement element in arcRolesRefNodes)
            {
                XNamespace ns = "http://www.w3.org/1999/xlink";
                string arcroleURI = (string)element.Attribute("arcroleURI") ?? string.Empty;
                XAttribute attrHref = element.Attribute(ns + "href");

                if (attrHref != null)
                {
                    this.ArcRoleRefs.Add(new ArcRoleRef(instance, attrHref.Value, arcroleURI));

                    HyperlinkReference xp = new HyperlinkReference(attrHref.Value);

                    if (!(xp.Url.Length == 0))
                    {                       
                        if (!ContainSchema(xp.Url))
                        {
                            log.Info("Creating Schema in arcroleref: " + xp.Url);

                            try
                            {
                                Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + xp.Url));
                                TaxonomySchemas.Add(new TaxonomySchema(this, xp.Url, instance, "", this.resolver));
                            }
                            catch (Exception)
                            {
                                log.Error("Error in create Schema in arcroleref(" + arcroleURI + "): " + xp.Url);
                                continue;
                            }
                        }
                    }
                }
            }
        }


        private void DiscoverySchemaInRoleRefLinkbase()
        {
            foreach (LinkbaseDocument linkbase in LinkbaseDocuments)
            {
                //RoleRef
                IEnumerable<XElement> rolesRefNodes = from c in linkbase.ThisXDocument.Root.Elements()
                                                      where c.Name.LocalName.Equals("roleRef")
                                                     select c;

                foreach (XElement element in rolesRefNodes)
                {
                    XNamespace ns = Constants.XLINK;
                    string roleURI = (string)element.Attribute("roleURI") ?? string.Empty;
                    XAttribute attrHref = element.Attribute(ns + "href"); 

                    if (attrHref != null)
                    {
                        this.RoleRefs.Add(new RoleRef(linkbase, attrHref.Value, roleURI));

                        HyperlinkReference xp = new HyperlinkReference(attrHref.Value);

                        if (!(xp.Url.Length == 0))
                        {                            
                            if (!ContainSchema(xp.Url))
                            {
                                log.Info("Creating Schema in roleref: " + xp.Url);

                                //if (xp.Url == "http://www.xbrl.org/lrr/role/deprecated-2009-12-16.xsd")
                                //{
                                //    string teste = "teste";
                                //}

                                try
                                {
                                    Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + xp.Url));
                                    TaxonomySchemas.Add(new TaxonomySchema(this, xp.Url, linkbase, "", this.resolver));
                                }
                                catch (Exception)
                                {
                                    log.Error("Error in create Schema in roleref(" + roleURI + "): " + xp.Url);
                                    continue;
                                }
                                
                            }
                        }
                    }
                }
            }
        }


        private void DiscoverySchemaInArcRoleRefLinkbase()
        {
            foreach (LinkbaseDocument linkbase in LinkbaseDocuments)
            {
                //ArcRoleRef
                IEnumerable<XElement> arcRolesRefNodes = from c in linkbase.ThisXDocument.Root.Elements()
                                                      where c.Name.LocalName.Equals("arcroleRef")
                                                      select c;

                foreach (XElement element in arcRolesRefNodes)
                {
                    XNamespace ns = Constants.XLINK;
                    string arcroleURI = (string)element.Attribute("arcroleURI") ?? string.Empty;
                    XAttribute attrHref = element.Attribute(ns + "href"); 

                    if (attrHref != null)
                    {
                        this.ArcRoleRefs.Add(new ArcRoleRef(linkbase, attrHref.Value, arcroleURI));

                        HyperlinkReference xp = new HyperlinkReference(attrHref.Value);

                        if (!(xp.Url.Length == 0))
                        {                            
                            if (!ContainSchema(xp.Url))
                            {
                                log.Info("Creating Schema in arcroleref: " + xp.Url);

                                try
                                {
                                    Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + xp.Url));
                                    TaxonomySchemas.Add(new TaxonomySchema(this, xp.Url, linkbase, "", this.resolver));
                                }
                                catch (Exception)
                                {
                                    log.Error("Error in create Schema in arcroleref(" + arcroleURI + "): " + xp.Url);
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Discovery of schema in LinkbaseRef.
        /// </summary>
        public void DiscoverySchemaInLinkbaseRef(TaxonomySchema taxonomySchema)
        {
            //LinkbaseRef
            foreach (XmlSchemaObject item in taxonomySchema.XmlSchema.Items)
            {
                if (item is XmlSchemaAnnotation)
                {
                    foreach (var annotation in ((XmlSchemaAnnotation)item).Items)
                    {
                        if (annotation is XmlSchemaAppInfo)
                        {
                            XmlSchemaAppInfo appinfo = annotation as XmlSchemaAppInfo;
                            foreach (XmlNode element in appinfo.Markup)
                            {
                                //LinkbaseRef
                                if (element.LocalName.Equals("linkbaseRef"))
                                {
                                    XmlNode attrHref = element.Attributes.GetNamedItem("xlink:href");

                                    if (attrHref != null)
                                    {
                                        HyperlinkReference xp = new HyperlinkReference(attrHref.Value);

                                        if (!(xp.Url.Length == 0))
                                        {
                                            if (!ContainSchema(xp.Url))
                                            {
                                                log.Info("Creating Schema in linkbaseRef: " + xp.Url);

                                                try
                                                {
                                                    Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Loading " + xp.Url));
                                                    TaxonomySchemas.Add(new TaxonomySchema(this, xp.Url, taxonomySchema, "", this.resolver));
                                                }
                                                catch (Exception)
                                                {
                                                    log.Error("Error in create Schema in " + taxonomySchema.FileName + ":linkbaseRef: " + xp.Url);
                                                    continue;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Section 3.5.3.9.7.3 of the XBRL 2.1 Specification
        /// All arcs in a DTS are grouped into base sets of arcs. All arcs in a base set of arcs:
        /// · have the same local name, namespace and xlink:arcrole attribute value on the arc element; and
        /// · are contained in extended link elements that have the same local name, namespace, and xlink:role attribute value.
        /// </summary>
        private void BuildBaseSetsofArcs()
        {
            ValidateLinkbaseInstance();

            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Building BaseSets of Arcs..."));

            foreach (LinkbaseDocument linkbase in LinkbaseDocuments)
            {
                foreach (ExtendedLink extendedLink in linkbase.ExtendedLinks)
                {
                    String extendedLinkRole = extendedLink.ExtendedLinkRole.AbsoluteUri;
                    string localNameExtendedLink = extendedLink.LocalName;
                    string namespaceUri = extendedLink.NamespaceUri;

                    if (!namespaceUri.Equals("http://xbrl.org/2008/generic"))
                    {
                        foreach (Arc arc in extendedLink.Arcs)
                        {
                            string localNameArc = arc.LocalName;
                            string arcRole = arc.ArcRole.AbsoluteUri;
                            baseSetsArcs.Add(new Tuple<string, string, string, string>(localNameExtendedLink, extendedLinkRole, localNameArc, arcRole), arc);
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Section 3.5.3.9.7.5 of the XBRL 2.1 Specification
        /// The rules of prohibiting and overriding relationships employ the use and priority attributes on arcs and the notion of 
        /// relationship equivalence to determine, for each relationship expressed by arcs in a base set, if that relationship is 
        /// included in the network of relationships for that base set of arcs.
        /// </summary>
        private void BuildNetworkRelationships()
        {
            Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Building Network Relationships..."));

            foreach (Tuple<string, string, string, string> tuple in baseSetsArcs.Keys)
            {
                if (!tuple.Item1.Equals("labelLink") && !tuple.Item1.Equals("referenceLink"))
                {
                    HashSet<Arc> networkRelationship = baseSetsArcs[tuple];
                    List<Arc> tempNetworkRelationship = new List<Arc>(networkRelationship);

                    HashSet<Arc> arcsExcluded = new HashSet<Arc>();

                    //for (int i = 0; i < networkRelationship.Count; i++)
                    //{
                    //    for (int j = 0; j < networkRelationship.Count; j++)
                    //    {
                    //        if (i != j)
                    //        {
                    //            //isProhibited
                    //            if (networkRelationship[i].IsProhibitedByArc(networkRelationship[j]))
                    //            {
                    //                if (networkRelationship.Contains(networkRelationship[i]))
                    //                {
                    //                    log.Debug("Removing prohibited arc: " + networkRelationship[i].LocalName + "-" + networkRelationship[i].FromId + " to " + networkRelationship[i].ToId + "-" + networkRelationship[i].ArcRole);
                    //                    arcsExcluded.Add(networkRelationship[i]);
                    //                }

                    //                if (networkRelationship.Contains(networkRelationship[j]))
                    //                {
                    //                    log.Debug("Removing prohibiting arc: " + networkRelationship[j].LocalName + "-" + networkRelationship[j].FromId + " to " + networkRelationship[j].ToId + "-" + networkRelationship[j].ArcRole);
                    //                    arcsExcluded.Add(networkRelationship[j]);
                    //                }
                    //            }

                    //            //isOverridden
                    //            if (networkRelationship[i].IsOverriddenByArc(networkRelationship[j]))
                    //            {
                    //                if (networkRelationship.Contains(networkRelationship[i]))
                    //                {
                    //                    log.Debug("Removing overridden arc: " + networkRelationship[i].LocalName + "-" + networkRelationship[i].FromId + " to " + networkRelationship[i].ToId + "-" + networkRelationship[i].ArcRole);
                    //                    arcsExcluded.Add(networkRelationship[i]);
                    //                }
                    //            }
                    //        }
                    //    }
                    //}

                    //networkRelationship.RemoveAll(arc => arcsExcluded.Contains(arc));

                    foreach (Arc arc in tempNetworkRelationship)
                    {
                        foreach (Arc compareArc in tempNetworkRelationship)
                        {
                            if (arc != compareArc)
                            {
                                //isProhibited
                                if (arc.IsProhibitedByArc(compareArc))
                                {
                                    if (networkRelationship.Contains(arc))
                                    {
                                        log.Debug("Removing prohibited arc: " + arc.LocalName + "-" + arc.FromId + " to " + arc.ToId + "-" + arc.ArcRole);
                                        networkRelationship.Remove(arc);
                                    }
                                    if (networkRelationship.Contains(compareArc))
                                    {
                                        log.Debug("Removing prohibiting arc: " + compareArc.LocalName + "-" + compareArc.FromId + " to " + compareArc.ToId + "-" + compareArc.ArcRole);
                                        networkRelationship.Remove(compareArc);
                                    }
                                }

                                //isOverridden
                                if (arc.IsOverriddenByArc(compareArc))
                                {
                                    if (networkRelationship.Contains(arc))
                                    {
                                        log.Debug("Removing overridden arc: " + arc.LocalName + "-" + arc.FromId + " to " + arc.ToId + "-" + arc.ArcRole);
                                        networkRelationship.Remove(arc);
                                    }
                                }
                            }
                        }
                    }

                    networkRelationships[tuple] = networkRelationship;
                    NetworkGraph ng = new NetworkGraph(tuple, networkRelationship, GetDistinctLocators(networkRelationship));
                    networkGraphs.Add(ng);
                }
            }
        }


        /// <summary>
        /// Validate linkbase document.
        /// </summary>
        private void ValidateLinkbaseInstance()
        {
            ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationHandler);

            XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();

            foreach (TaxonomySchema taxonomySchema in TaxonomySchemas)
            {
                VerifySchemasDuplicates(xmlSchemaSet, taxonomySchema.XmlSchemaSet);
                xmlSchemaSet.Add(taxonomySchema.XmlSchema);
            }

            RemoveSchemaDuplicate(xmlSchemaSet);

            foreach (LinkbaseDocument linkbase in LinkbaseDocuments)
            {
                if (!linkbase.RoleLinkbaseRef.Equals("http://www.xbrl.org/2003/role/labelLinkbaseRef") && 
                    !linkbase.RoleLinkbaseRef.Equals("http://www.xbrl.org/2003/role/referenceLinkbaseRef"))
                {
                    try
                    {
                        //xmlSchemaSet.Add("http://www.xbrl.org/2003/linkbase", "http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd");
                        //xmlSchemaSet.Add("http://xbrl.org/2008/generic", "http://www.xbrl.org/2008/generic-link.xsd");                    

                        //log.Info("Validating linkbase: " + linkbase.FileName + " targetNamespace:" + linkbase.TargetNamespace);
                        Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Parsing linkbase: " + linkbase.FileName + " arcs:" + linkbase.ExtendedLinks.Count));
                        linkbase.ThisXDocument.Validate(xmlSchemaSet, eventHandler, true);
                    }
                    catch (Exception ex)
                    {
                        log.Error("Validation error on linkbase: " + linkbase.FileName + "." + ex.Message);
                        ValidatedLinkbases = false;
                    }
                }
            }
        }


        private void ValidationHandler(object sender, ValidationEventArgs e)
        {
            string name = GetFileName((sender as XObject).BaseUri);
            IXmlLineInfo lineInfo = ((sender as XObject) as IXmlLineInfo);
            log.Error("Validation error on linkbase \"" + name + "\", line " + lineInfo.LineNumber + ", position " + lineInfo.LinePosition + "." + e.Message);
            ValidatedLinkbases = false;
        }


        public void VerifySchemasDuplicates(XmlSchemaSet xmlSchemaSetDTS, XmlSchemaSet xmlSchemaSet)
        {
            // Using an ArrayList lets use indexing for accessing the schemas.
            ArrayList schemaListDTS = new ArrayList(xmlSchemaSetDTS.Schemas());

            // ArrayList holding the duplicate schemas.
            ArrayList schemaList = new ArrayList(xmlSchemaSet.Schemas());

            for (int i = 0; i < schemaListDTS.Count; i++)
            {
                // Exhaustively test for duplicate schemas by means of the
                // SourceUri property.
                for (int j = i + 1; j < schemaList.Count; j++)
                {
                    if (((XmlSchema)schemaListDTS[i]).TargetNamespace == ((XmlSchema)schemaList[j]).TargetNamespace &&
                        ((XmlSchema)schemaListDTS[i]).SourceUri == ((XmlSchema)schemaList[j]).SourceUri)
                    {
                        xmlSchemaSet.Remove((XmlSchema)schemaList[j]);
                    }
                }
            }
        }


        private static void RemoveSchemaDuplicate(XmlSchemaSet xmlSchemaSet)
        {
            // Using an ArrayList lets use indexing for accessing the schemas.
            ArrayList schemaList = new ArrayList(xmlSchemaSet.Schemas());

            // ArrayList holding the duplicate schemas.
            ArrayList duplicateSchemaList = new ArrayList();

            for (int i = 0; i < schemaList.Count; i++)
            {                
                // Exhaustively test for duplicate schemas by means of the
                // SourceUri property.
                for (int j = i + 1; j < schemaList.Count; j++)
                {
                    if (((XmlSchema)schemaList[i]).TargetNamespace ==
                        ((XmlSchema)schemaList[j]).TargetNamespace)
                    {
                        //duplicateSchemaList.Add((XmlSchema)schemaList[j]);
                        xmlSchemaSet.Remove((XmlSchema)schemaList[j]);
                    }                    
                }
            }
        }


        /// <summary>
        /// Add schema if not duplicate.
        /// </summary>
        public XmlSchemaSet AddXmlSchemaSet(XmlSchemaSet xmlSchemaSet, XmlSchema xmlSchema)
        {
            XmlSchemaSet schemaSet1 = new XmlSchemaSet();

            //verify in import and include elements.
            foreach (XmlSchema schemaDTS in xmlSchemaSet.Schemas())
            {
                if (!schemaDTS.SourceUri.Equals(xmlSchema.SourceUri))
                {
                    schemaSet1.Add(xmlSchema);
                }

                //verify in import and include elements.
                foreach (XmlSchemaObject schemaInclude in xmlSchema.Includes)
                {
                    XmlSchema externalSchema = null;

                    if (schemaInclude is XmlSchemaImport)
                    {
                        externalSchema = ((XmlSchemaImport)schemaInclude).Schema;

                        if (!externalSchema.SourceUri.Equals(schemaDTS.SourceUri))
                        {
                            schemaSet1.Add(externalSchema);
                        }
                    }

                    if (schemaInclude is XmlSchemaInclude)
                    {
                        externalSchema = ((XmlSchemaInclude)schemaInclude).Schema;

                        if (!externalSchema.SourceUri.Equals(schemaDTS.SourceUri))
                        {
                            schemaSet1.Add(externalSchema);
                        }
                    }
                }
            }

            return schemaSet1;
        }


        internal static XmlSchemaSet MergeSchemas(XmlSchemaSet schema1, XmlSchemaSet schema2)
        {
          XmlSchemaSet schemaSet1 = new XmlSchemaSet();
          schemaSet1.Add(schema1);
          schemaSet1.Compile();

          XmlSchemaSet schemaSet2 = new XmlSchemaSet();
          schemaSet2.Add(schema2);
          schemaSet2.Compile();

          foreach (XmlSchemaElement el1 in schemaSet1.GlobalElements.Values)
          {
            foreach (XmlSchemaElement el2 in schemaSet2.GlobalElements.Values)
            {
              if (el2.QualifiedName.Equals(el1.QualifiedName))
              {
                ((XmlSchema)el2.Parent).Items.Remove(el2);
                break;
              }
            }
          }

          foreach (XmlSchema schema in schemaSet2.Schemas())
          {
            schemaSet2.Reprocess(schema);
          }
          schemaSet2.Compile();
          schemaSet1.Add(schemaSet2);

          return schemaSet1;
        }


        internal bool IsCustomArcRole(string arcRole)
        {
            foreach (ArcRoleType arcRoleType in ArcRoleTypes)
            {
                if (arcRole.Equals(arcRoleType.ArcRoleUri.ToString()))
                {
                    return true;
                }
            }

            return false;
        }


        internal ArcRoleType GetArcRoleType(string arcRole)
        {
            foreach (ArcRoleType arcRoleType in ArcRoleTypes)
            {
                if (arcRoleType.ArcRoleUri.ToString().Equals(arcRole))
                {
                    return arcRoleType;
                }
            }

            return null;
        }


        internal RoleType GetRoleType(string id)
        {
            foreach (RoleType roleType in RoleTypes)
            {
                if (roleType.Id.Equals(id))
                {
                    return roleType;
                }
            }

            return null;
        }


        /// <summary>
        /// Returns a Concept object to a specific ID from the DTS. All the
        /// taxonomies belonging to this DTS are scanned for the element.
        /// </summary>
        /// <param name="id">
        ///            The ID for which the element shall be obtained. </param>
        /// <returns> The according Concept object from the DTS. If the concept is not
        ///         found in any taxonomy of the DTS, null is returned. </returns>
        public virtual Concept GetConceptByID(string id)
        {
            foreach (TaxonomySchema schema in TaxonomySchemas)
	        {
                Concept tmpElement = schema.GetConceptByID(id);

                if (tmpElement != null)
                {
                    return tmpElement;
                }
            }

            return null;
        }


        /// <summary>
        /// Returns a Concept object to a specific Name from the DTS. All the
        /// taxonomies belonging to this DTS are scanned for the element.
        /// </summary>
        /// <param name="name">
        ///            The Name for which the element shall be obtained. </param>
        /// <returns> The according Concept object from the DTS. If the concept is not
        ///         found in any taxonomy of the DTS, null is returned. </returns>
        public virtual Concept GetConceptByName(string ns, string name)
        {
            foreach (TaxonomySchema schema in TaxonomySchemas)
            {
                Concept tmpElement = schema.GetConceptByName(ns, name);

                if (tmpElement != null)
                {
                    return tmpElement;
                }
            }

            return null;
        }


        public virtual Concept GetConceptByName(string name)
        {
            foreach (TaxonomySchema schema in TaxonomySchemas)
            {
                Concept tmpElement = schema.GetConceptByName(name);

                if (tmpElement != null)
                {
                    return tmpElement;
                }
            }

            return null;
        }


        /// <summary>
        /// Returns a list of all the concepts belonging to this DTS (Set of Concept
        /// objects).
        /// </summary>
        /// <returns> Set with all the concepts of all taxonomies belonging to this
        ///         DTS. </returns>
        public virtual List<Concept> GetAllConcepts()
        {
            List<Concept> returnSet = new List<Concept>();

            foreach (TaxonomySchema schema in TaxonomySchemas)
            {
                returnSet.AddRange(schema.Concepts);      
            }

            return returnSet;
 
        }


        public List<Locator> GetDistinctLocators(HashSet<Arc> networkRelationship)
        {
            List<Locator> tempLocators = new List<Locator>();

            foreach (Arc arc in networkRelationship)
            {
                if (!tempLocators.Exists(l => l.Href.Equals(arc.FromLocator.Href) && l.Label.Equals(arc.FromLocator.Label)))
                {
                    SetLocatorArcs(arc.FromLocator, networkRelationship);
                    tempLocators.Add(arc.FromLocator);
                }

                foreach (Locator locator in arc.ToLocators)
                {
                    if (!tempLocators.Exists(l => l.Href.Equals(locator.Href) && l.Label.Equals(locator.Label)))
                    {
                        SetLocatorArcs(locator, networkRelationship);
                        tempLocators.Add(locator);
                    }
                }
            }

            return tempLocators;
        }


        private void SetLocatorArcs(Locator loc, HashSet<Arc> networkRelationship)
        {
            foreach (Arc arc in networkRelationship)
            {
                if (loc.Label.Equals(arc.FromId))
                {
                    loc.OutArcs.AddLast(arc);
                }

                if (loc.Label.Equals(arc.ToId))
                {
                    loc.InArcs.AddLast(arc);
                }
            }
        }


        public Locator GetLocator(IndexedList<Locator> locators, string label)
        {
            return locators.ContainsKeyByName(label.GetHashCode()) ? locators.GetItemByName(label.GetHashCode()) : null;

            //foreach (Locator loc in locators)
            //{
            //    if (loc.Label.Equals(label))
            //    {
            //        return loc;
            //    }
            //}

            //return null;
        }


        /// <summary>
        /// Locates all items in the fragment that match the current element.
        /// </summary>
        /// <param name="concept">
        /// The element describing the items to be found.
        /// </param>
        /// <returns>
        /// A collection of items that match the current element. If no items match,
        /// a non-null List will still be returned, but the list will be empty.
        /// </returns>
        /// <remarks>
        /// This method should most likely be moved into a class which wraps <see cref="FactItem"/>
        /// collections with a value-added wrapper class.
        /// </remarks>
        public List<FactItem> LocateItems(Concept concept)
        {
            return LocateItems(concept, Instance.Facts);
        }


        /// <summary>
        /// Locates all items in the given collection of facts that match the current element.
        /// </summary>
        /// <param name="concept">
        /// The element describing the items to be found.
        /// </param>
        /// <param name="factList">
        /// The collection of items that should be searched.
        /// </param>
        /// <returns>
        /// A collection of items that match the current element. If no items match,
        /// a non-null List will still be returned, but the list will be empty.
        /// </returns>
        /// <remarks>
        /// This method should most likely be moved into a class which wraps <see cref="FactItem"/>
        /// collections with a value-added wrapper class.
        /// </remarks>
        public List<FactItem> LocateItems(Concept concept, List<Fact> factList)
        {
            var itemList = new List<FactItem>();

            if (concept != null)
            {
                foreach (Fact currentFact in factList)
                {
                    if ((currentFact is FactItem))
                    {
                        var currentItem = currentFact as FactItem;

                        if (currentItem.Concept.Equals(concept))
                        {
                            itemList.Add(currentItem);
                        }
                    }
                    else if ((currentFact is WMM.Tapioca.Instance.FactTuple))
                    {
                        var currentTuple = currentFact as WMM.Tapioca.Instance.FactTuple;
                        var tupleList = LocateItems(concept, currentTuple.Facts);
                        itemList.AddRange(tupleList);
                    }
                }
            }

            return itemList;
        }


        public List<Fact> LocateFacts(Concept concept, List<Fact> searchFactList)
        {
            var factList = new List<Fact>();

            if (concept != null)
            {
                foreach (Fact currentFact in searchFactList)
                {
                    if ((currentFact is FactItem))
                    {
                        var currentItem = currentFact as FactItem;

                        if (currentItem.Concept.Equals(concept))
                        {
                            factList.Add(currentFact);
                        }
                    }
                    else if ((currentFact is WMM.Tapioca.Instance.FactTuple))
                    {
                        var currentTuple = currentFact as WMM.Tapioca.Instance.FactTuple;
                        var tupleList = LocateFacts(concept, currentTuple.Facts);
                        factList.AddRange(tupleList);
                    }
                }
            }

            return factList;
        }


        private void BuildSummation()
        {
            foreach (NetworkGraph ng in this.NetworkGraphs)
            {
                if (ng.TupleNG.Item1.Equals("calculationLink"))
                {
                    Controller.Instance.RaiseModelChange(this, new ModelChangeEventArgs("Building Summation, roleUri:" + ng.TupleNG.Item2));

                    foreach (Arc currentCalculationArc in ng.NetworkRelationships)
                    {
                        //ResolveLocators(ng.DistinctLocators, CurrentCalculationArc);
                        BuildSummationConcepts(ng, currentCalculationArc);
                    }
                }
            }
        }


        private void BuildSummationConcepts(NetworkGraph ng, Arc currentCalculationArc)
        {
            Summation currentSummation;

            currentSummation = FindSummation(ng, currentCalculationArc.FromLocator);

            if (currentSummation == null)
            {
                currentSummation = new Summation(ng, currentCalculationArc.FromLocator);
                ng.SummationConcepts.Add(currentSummation);
            }

            foreach (var currentToLocator in currentCalculationArc.ToLocators)
            {
                currentSummation.AddContributingConceptLocator(currentToLocator);
            }
        }


        private Summation FindSummation(NetworkGraph ng, Locator summationConceptLocator)
        {
            foreach (Summation currentSummation in ng.SummationConcepts)
            {
                if (currentSummation.SummationLocator.Equals(summationConceptLocator))
                {
                    return currentSummation;
                }
            }

            return null;
        }
    }
}
